package com.jxtc.enterprise.merchant.service;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jxtc.enterprise.admin.dto.FoodSpecificationDTO;
import com.jxtc.enterprise.admin.dto.FoodSpecificationItemDTO;
import com.jxtc.enterprise.admin.vo.FoodVO;
import com.jxtc.enterprise.merchant.dto.FoodUpdateDTO;
import com.jxtc.enterprise.merchant.dto.FoodCreateDTO;
import com.jxtc.enterprise.common.entity.Food;
import com.jxtc.enterprise.common.entity.Store;
import com.jxtc.enterprise.common.exception.ModifyFailureException;
import com.jxtc.enterprise.common.mapper.FoodMapper;
import com.jxtc.enterprise.common.mapper.StoreMapper;
import com.jxtc.enterprise.common.vo.PaginationResponseVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class MerchantFoodManageService {

    @Autowired
    private FoodMapper foodMapper;

    @Autowired
    private final StoreMapper storeMapper;

    /**
     * 根据关键字或店铺 ID 分页查询餐品
     *
     * @param foodName 菜品名
     * @param storeId 店铺 ID
     * @param foodStatus 菜品上架状态，有效取值 1 上架中，2 下架中
     * @param currentPage 分页参数，当前页编号
     * @param pageSize    分页参数，页大小，每页显示的数量
     * @return 分页查询结果
     */
    public PaginationResponseVO<JSONObject> pageFoodByStoreIdFoodNameOrFoodStatus(String storeId, String foodName, Integer foodStatus, int currentPage, int pageSize) {
        Page<Food> page = new Page<>(currentPage, pageSize);

        QueryWrapper<Food> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag", 0);

        if (StringUtils.isNotEmpty(foodName)) {
            wrapper.like("name", foodName);
        }

        if (StringUtils.isNoneEmpty(storeId)) {
            wrapper.eq("store_id", storeId);
        }

        // 菜品上架状态有效取值为1和2，除了1和2，传递其他值，都不对菜品的上架或下架状态进行过滤
        if (foodStatus.equals(1) || foodStatus.equals(2)) {
            wrapper.eq("status", foodStatus);
        }

        Page<Food> result = foodMapper.selectPage(page, wrapper);
        // 使用 FoodVO 的构造方法将数据库中的信息转换成对应的 VO
        List<JSONObject> records = result.getRecords().stream().map(food -> {
            JSONObject formattedFoodInfo = new JSONObject();
            formattedFoodInfo.put("foodId", food.getId());
            formattedFoodInfo.put("foodName", food.getName());
            formattedFoodInfo.put("foodImage", food.getImage());
            formattedFoodInfo.put("description", food.getDescription());
            float formattedStorePrice = Float.valueOf(food.getStorePrice()) / 100;
            formattedFoodInfo.put("storePrice",  String.format("%.2f", formattedStorePrice));
            formattedFoodInfo.put("foodTypeName",  food.getFoodTypeName());
            formattedFoodInfo.put("status",  food.getStatus());
            formattedFoodInfo.put("sortFlag",  food.getSortFlag());
            return formattedFoodInfo;
        }).sorted(Comparator.comparingInt(o -> o.getInteger("sortFlag"))) // 按照菜品排序参数排序, 升序排序
            .collect(Collectors.toList());

        return new PaginationResponseVO<>(result.getTotal(), result.getCurrent(), records);
    }

    /**
     * 查询某个店铺中，各种状态 上架中 或 下架中 的 菜品个数
     * @param storeId
     * @return
     */
    public JSONObject queryFoodCountOfEachStatus(String storeId, String foodName, Integer foodStatus) {

        JSONObject foodStatusMap = new JSONObject();
        List<HashMap<Integer,Integer>> foodCountOfStatusList = foodMapper.queryFoodCountOfStatus(storeId);
        JSONObject foodStatusCountMap = new JSONObject();
        // 先统计出 上架 或 下架 的菜品的个数
        foodCountOfStatusList.stream().map(item -> foodStatusCountMap.put(String.valueOf(item.get("foodStatus")), item.get("count"))).collect(Collectors.toList());
        Long onSale = (Long) foodStatusCountMap.getOrDefault("1", 0L);
        Long offSale = (Long) foodStatusCountMap.getOrDefault("2", 0L);
        // 在计算当前被选中页签对应的状态的菜品个数；当前被选中页签上显示的菜品个数，是通过搜索词过滤后的 菜品的个数；
        // 另外一个未被选中的页签上显示的菜品个数，则不通过搜索内容过滤，仅通过上架或下架状态过滤
        Long foodCount = foodMapper.selectCount(
                new LambdaQueryWrapper<Food>()
                        .eq(Food::getDelFlag, 0)
                        .eq(Food::getStoreId, storeId)
                        .eq(Food::getStatus, foodStatus)
                        .like(Food::getName, foodName));
        // 当前被选中页签的菜品个数，替换成 添加了菜品名 搜索词过滤后的菜品个数
        if (foodStatus.equals(1)) {
            onSale = foodCount;
        } else if (foodStatus.equals(2)) {
            offSale = foodCount;
        }

        foodStatusMap.put("onSale", onSale);
        foodStatusMap.put("offSale", offSale);

        return foodStatusMap;
    }

    /**
     * 新增菜品
     * @param dto
     * @return
     */
    public JSONObject saveFood(FoodCreateDTO dto) {
        // 将前端传递的数据转换成符合要求的餐品信息
        Food food = dtoToFood(dto);

        if (foodMapper.insert(food) <= 0) {
            throw new ModifyFailureException("新增餐品失败，请稍后重试");
        }

        JSONObject newCreatedFoodInfo = new JSONObject();
        newCreatedFoodInfo.put("foodId", food.getId());
        newCreatedFoodInfo.put("foodName", food.getName());
        return newCreatedFoodInfo;
    }

    /**
     * 将前端传递的数据转换成符合要求的餐品信息，如金额单位转换，标志位转换
     *
     * @param dto 前端传递的数据
     * @return 符合要求的餐品信息
     */
    private Food dtoToFood(FoodCreateDTO dto) {
        Food food = new Food();
        food.setName(dto.getFoodName().trim());
        food.setImage(dto.getFoodImage());

        // 如果前端传递的店铺 ID 不存在，给出提示信息
        Store store = storeMapper.selectById(dto.getStoreId());
        if (store == null) {
            throw new IllegalArgumentException("店铺 ID 不存在");
        }
        food.setStoreName(store.getName());
        food.setStoreId(dto.getStoreId());

        food.setFoodTypeName(dto.getFoodTypeName());

        // 将前端传递的金额（字符串类型）转换成数字类型，并乘以 100 将价格的单位转换成：分
        BigDecimal storePrice = new BigDecimal(dto.getStorePrice()).multiply(new BigDecimal(100));
        food.setStorePrice(storePrice.intValue());

        food.setSortFlag(dto.getSortFlag());
        food.setDescription(dto.getDescription());

        // 如果当前菜品为上架状态，则状态标志置为 1
        food.setStatus(dto.getStatus());

        food.setRequired(dto.getRequired());
        // 通过商户小程序添加菜品时，接口中规格值中无salePrice，需要进行补充默认值
        padSalePriceForFoodSpecificationSubItemsWhenAddNewFood(dto.getSpecification());
        food.setSpecification(JSONUtil.toJsonStr(dto.getSpecification()));

        return food;
    }

    /**
     * 更新菜品信息
     * @param dto
     */
    public void updateFoodById(FoodUpdateDTO dto) {
        Food food = dtoToFood(dto);
        food.setId(dto.getFoodId());

        if (foodMapper.updateById(food) <= 0) {
            throw new ModifyFailureException("修改餐品信息失败，请稍后重试");
        }
    }

    /**
     * 查询店铺的 菜品类型
     * @param storeId
     * @return
     */
    public String queryFoodTypesByStoreId(String storeId) {
        // 此处使用 selectList() 而不是 selectOne()， 当表中有多条id或0条匹配的记录时，selectOne() 会抛出异常，使用selectList() 可以正常返回数据，然后打印warning日志，先保证接口正常响应，然后再进行排查
        List<Store> stores = storeMapper.selectList(new LambdaQueryWrapper<Store>().eq(Store::getId, storeId).eq(Store::getDelFlag, 0));
        Store store = null;
        if (!CollectionUtils.isEmpty(stores)) {
            store = stores.get(0);
            if (stores.size() > 1) {
                log.warn("店铺id: {} 在表中有{} 条记录，需要排查下", storeId, stores.size());
            }
        }

        return store.getFoodTypes();
    }

    /**
     * 查询菜品详情
     * @param storeId
     * @param foodId
     * @return
     */
    public JSONObject queryFoodDetailByFoodId(String storeId, String foodId) {

        FoodVO foodVo = null;
        List<Food> foods = foodMapper.selectList(new LambdaQueryWrapper<Food>().eq(Food::getStoreId, storeId).eq(Food::getId, foodId).eq(Food::getDelFlag, 0));
        if (!CollectionUtils.isEmpty(foods)) {
            foodVo = new FoodVO(foods.get(0));
            if (foods.size() > 1) {
                log.warn("菜品id: {} 在表中有{} 条记录，需要排查下", foodId, foods.size());
            }
        } else {
            return null;
        }

        JSONObject formmated = new JSONObject();
        formmated.put("foodId", foodVo.getFoodId());
        formmated.put("foodName", foodVo.getFoodName());
        formmated.put("foodImage", foodVo.getFoodImage());
        formmated.put("description", foodVo.getDescription());
        // 当规格值中没有 salePrice时，基于 storePrice 补充值
        padSalePriceForFoodSpecificationSubItems(foodVo.getSpecification());
        formmated.put("specification", foodVo.getSpecification());
        formmated.put("storeId", foodVo.getStoreId());
        formmated.put("storeName", foodVo.getStoreName());
        formmated.put("storePrice", foodVo.getStorePrice());
        formmated.put("foodTypeName", foodVo.getFoodTypeName());
        formmated.put("status", foodVo.getState() ? 1: 2); // 1 上架中，2下架中
        formmated.put("sortFlag", foodVo.getSort());
        formmated.put("required", foodVo.getRequired());

        return formmated;
    }

    /**
     * 根据 storeId 和 foodId 查询指定菜品，并根据 status 的值判断上架还是下架操作
     *
     * @param storeId 店铺 ID
     * @param foodId  菜品 ID
     * @param status  菜品上架还是下架：1 上架，2 下架
     */
    public void changeFoodStatusByStoreIdAndFoodId(String storeId, String foodId, int status) {
        LambdaUpdateWrapper<Food> updateWrapper = new LambdaUpdateWrapper<Food>()
                .eq(Food::getStoreId, storeId)
                .eq(Food::getId, foodId)
                .eq(Food::getDelFlag, false);

        Food food = new Food();
        // 修改菜品的上架/下架状态
        food.setStatus(status);

        // 更新菜品上架/下架状态
        int updateCount = foodMapper.update(food, updateWrapper);
        log.info("/changeFoodStatus, update rows: {}", updateCount);

        // 如果没有修改数据，给出错误提示信息
        if (updateCount <= 0) {
            String tipMessage = "店铺 ID 为 " + storeId + " 且餐品 ID 为 " + foodId + " 的餐品不存在，请联系管理员排查";
            throw new IllegalArgumentException(tipMessage);
        }
    }

    /**
     * 根据 storeId 和 foodId 查询指定菜品，将其删除标志置为 "已删除" 状态，并设置菜品的 "上架/下架" 状态为 "已下架 状态
     *
     * @param storeId 店铺 ID
     * @param foodId  菜品 ID
     */
    public void deleteFoodByStoreIdAndFoodId(String storeId, String foodId) {
        LambdaUpdateWrapper<Food> updateWrapper = new LambdaUpdateWrapper<Food>()
                .eq(Food::getStoreId, storeId)
                .eq(Food::getId, foodId)
                .eq(Food::getDelFlag, false);

        Food food = new Food();
        // 修改菜品为下架状态
        food.setStatus(2);
        // 修改菜品为已删除状态
        food.setDelFlag(true);

        // 更新菜品状态
        int updateCount = foodMapper.update(food, updateWrapper);
        log.info("/deleteFood, update rows: {}", updateCount);

        // 如果没有修改数据，给出错误提示信息
        if (updateCount <= 0) {
            String tipMessage = "店铺 ID 为 " + storeId + " 且餐品 ID 为 " + foodId + " 的餐品不存在，请联系管理员排查";
            throw new IllegalArgumentException(tipMessage);
        }
    }

    /**
     * 根据 storeId 查询指定店铺，在原有菜品分类的基础上追加新的菜品分类，如果待新增的菜品分类已经存在，给出提示信息
     *
     * @param storeId      店铺 ID
     * @param foodTypeName 待新增的菜品分类名称
     */
    public void addNewFoodTypeByStoreId(String storeId, String foodTypeName) {
        LambdaQueryWrapper<Store> queryWrapper = new LambdaQueryWrapper<Store>()
                .eq(Store::getId, storeId)
                .eq(Store::getDelFlag, false);

        Store savedStore = storeMapper.selectOne(queryWrapper);
        if (savedStore == null) {
            throw new IllegalArgumentException("店铺 ID 为 " + storeId + " 的店铺不存在，请联系管理员排查");
        }

        // 将该店铺已有的菜品分类取出，并将 JSON Array 格式的数据转换成 List
        String savedFoodTypeStr = savedStore.getFoodTypes();
        log.info("/addNewFoodType, oldFoodType: {}", savedFoodTypeStr);
        List<String> savedFoodTypeList = JSONUtil.toList(savedFoodTypeStr, String.class);

        // 如果已有的菜品分类已经包含需要新增的菜品分类，则不新增，并给前端响应的提示信息
        if (savedFoodTypeList.contains(foodTypeName)) {
            String tipMessage = "ID 为 " + storeId + " 的店铺中已有【" + foodTypeName + "】分类，请勿重复添加";
            throw new IllegalArgumentException(tipMessage);
        }

        // 将新的菜品分类添加到已有分类中，并序列化成 JSON Array 格式
        savedFoodTypeList.add(foodTypeName);
        String newFoodTypeStr = JSONUtil.toJsonStr(savedFoodTypeList);
        log.info("/addNewFoodType, newFoodType: {}", newFoodTypeStr);

        Store store = new Store();
        store.setId(savedStore.getId());
        store.setFoodTypes(newFoodTypeStr);

        int updateCount = storeMapper.updateById(store);
        log.info("/addNewFoodType, update rows: {}", updateCount);

        // 如果没有修改数据，给出错误提示信息
        if (updateCount <= 0) {
            String tipMessage = "ID 为 " + storeId + " 的店铺新增菜品分类失败，请联系管理员排查";
            throw new IllegalArgumentException(tipMessage);
        }
    }

    /**
     * 根据 storeId 查询指定店铺，并将该店铺的菜品分类由 oldFoodTypeName 修改为 newFoodTypeName
     *
     * @param storeId         店铺 ID
     * @param newFoodTypeName 修改后的菜品分类
     * @param oldFoodTypeName 修改前的菜品分类
     */
    public void editFoodTypeByStoreId(String storeId, String newFoodTypeName, String oldFoodTypeName) {
        LambdaQueryWrapper<Store> queryWrapper = new LambdaQueryWrapper<Store>()
                .eq(Store::getId, storeId)
                .eq(Store::getDelFlag, false);

        Store savedStore = storeMapper.selectOne(queryWrapper);
        if (savedStore == null) {
            throw new IllegalArgumentException("店铺 ID 为 " + storeId + " 的店铺不存在，请联系管理员排查");
        }

        // 将该店铺已有的菜品分类取出，并将 JSON Array 格式的数据转换成 List
        String savedFoodTypeStr = savedStore.getFoodTypes();
        log.info("/editFoodType, oldFoodType: {}", savedFoodTypeStr);
        List<String> savedFoodTypeList = JSONUtil.toList(savedFoodTypeStr, String.class);

        // 如果已有的菜品分类已经包含修改后的的菜品分类，则不修改，并给前端响应的提示信息
        if (savedFoodTypeList.contains(newFoodTypeName)) {
            String tipMessage = "ID 为 " + storeId + " 的店铺中已有【" + newFoodTypeName + "】分类，请勿重复";
            throw new IllegalArgumentException(tipMessage);
        }

        // 如果已有的菜品分类不包含修改前的的菜品分类，则不修改，并给前端响应的提示信息
        if (!savedFoodTypeList.contains(oldFoodTypeName)) {
            String tipMessage = "ID 为 " + storeId + " 的店铺中不存在【" + oldFoodTypeName + "】分类，修改失败";
            throw new IllegalArgumentException(tipMessage);
        }

        // 删除旧的菜品分类，添加新的菜品分类，并序列化成 JSON Array 格式
        savedFoodTypeList.remove(oldFoodTypeName);
        savedFoodTypeList.add(newFoodTypeName);
        String newFoodTypeStr = JSONUtil.toJsonStr(savedFoodTypeList);
        log.info("/editFoodType, newFoodType: {}", newFoodTypeStr);

        Store store = new Store();
        store.setId(savedStore.getId());
        store.setFoodTypes(newFoodTypeStr);

        int updateCount = storeMapper.updateById(store);
        log.info("/editFoodType, update rows: {}", updateCount);

        // 如果没有修改数据，给出错误提示信息
        if (updateCount <= 0) {
            String tipMessage = "ID 为 " + storeId + " 的店铺修改菜品分类失败，请联系管理员排查";
            throw new IllegalArgumentException(tipMessage);
        }
    }

    /**
     * 根据 storeId 查询指定店铺，从菜品分类中删除名称为 foodTypeName 的菜品分类
     *
     * @param storeId      店铺 ID
     * @param foodTypeName 待删除的菜品分类名称
     */
    public void deleteFoodTypeByStoreId(String storeId, String foodTypeName) {
        LambdaQueryWrapper<Store> queryWrapper = new LambdaQueryWrapper<Store>()
                .eq(Store::getId, storeId)
                .eq(Store::getDelFlag, false);

        Store savedStore = storeMapper.selectOne(queryWrapper);
        if (savedStore == null) {
            throw new IllegalArgumentException("店铺 ID 为 " + storeId + " 的店铺不存在，请联系管理员排查");
        }

        // 将该店铺已有的菜品分类取出，并将 JSON Array 格式的数据转换成 List
        String savedFoodTypeStr = savedStore.getFoodTypes();
        log.info("/deleteFoodType, oldFoodType: {}", savedFoodTypeStr);
        List<String> savedFoodTypeList = JSONUtil.toList(savedFoodTypeStr, String.class);

        // 如果已有的菜品分类不包含需要删除的菜品分类，则放弃删除，并给前端响应的提示信息
        if (!savedFoodTypeList.contains(foodTypeName)) {
            String tipMessage = "ID 为 " + storeId + " 的店铺中不存在【" + foodTypeName + "】分类";
            throw new IllegalArgumentException(tipMessage);
        }

        // 从已有分类中删除菜品分类，并序列化成 JSON Array 格式
        savedFoodTypeList.remove(foodTypeName);
        String newFoodTypeStr = JSONUtil.toJsonStr(savedFoodTypeList);
        log.info("/deleteFoodType, newFoodType: {}", newFoodTypeStr);

        Store store = new Store();
        store.setId(savedStore.getId());
        store.setFoodTypes(newFoodTypeStr);

        int updateCount = storeMapper.updateById(store);
        log.info("/deleteFoodType, update rows: {}", updateCount);

        // 如果没有修改数据，给出错误提示信息
        if (updateCount <= 0) {
            String tipMessage = "ID 为 " + storeId + " 的店铺删除菜品分类失败，请联系管理员排查";
            throw new IllegalArgumentException(tipMessage);
        }
    }

    /**
     * 当某个菜品项的 规格值中，没有销售价格字段时，对其进行补充，在 storePrice 的基础上加 60% 作为销售价
     * @param specification
     */
    private void padSalePriceForFoodSpecificationSubItems(cn.hutool.json.JSONArray specification) {

        for (int i = 0; i < specification.size(); i++) {
            // 每个 一级规格 对象
            cn.hutool.json.JSONObject specificationL1Item = specification.getJSONObject(i);
            // 一级规格的二级规格列表
            JSONArray l2Items = specificationL1Item.getJSONArray("items");
            for (int k = 0; k < l2Items.size(); k++) {
                // 二级规格值
                cn.hutool.json.JSONObject l2ItemValue = l2Items.getJSONObject(k);
                if (!l2ItemValue.containsKey("salePrice")) {
                    // 只有当某个二级规格的规格值中，没有 salePrice，销售价格时，使用该规格的 storePrice合作价 + 30% ，作为销售价
                    int storePrice = l2ItemValue.getInt("storePrice");
                    // Double.intValue() 方法，向下取整，丢弃掉小数部分
                    int salePrice = Double.valueOf(storePrice * 1.6).intValue() ;
                    // 该工具类的说明文档中标明 put() 方法不建议使用，改为使用 set()
                    l2ItemValue.set("salePrice", salePrice);
                }
            }
        }
    }

    /**
     * 当商户通过商户小程序新增菜品时，菜品的规格信息中，销售价为空，需要补充，否则 通过PC管理平台查看 该商品时，会导致 规格信息缺失
     * @param specification
     */
    private void padSalePriceForFoodSpecificationSubItemsWhenAddNewFood(List<FoodSpecificationDTO> specification) {

        for (int i = 0; i < specification.size(); i++) {
            // 每个 一级规格 对象
            FoodSpecificationDTO specificationL1Item = specification.get(i);
            // 一级规格的二级规格列表; FoodSpecificationItemDTO 类中的 @Min() 注解无法校验前端传递null的情况; 当商户通过商户小程序编辑菜品时，没有填写 合作价，限购量
            // 则接口参数传递的是null，此处若不对null做判断并赋予默认孩子，则会导致表中的规格数据中，某些属性为null，会导致其他使用到的地方出现空指针异常
            List<FoodSpecificationItemDTO> l2Items = specificationL1Item.getItems();
            for (FoodSpecificationItemDTO l2ItemValue : l2Items) {
                // 二级规格值;
                if (l2ItemValue.getStorePrice() == null) {
                    l2ItemValue.setStorePrice(0d);
                }
                if (l2ItemValue.getSalePrice() == null) {
                    l2ItemValue.setSalePrice(0d);
                }
                if (l2ItemValue.getLimitCount() == null) {
                    l2ItemValue.setLimitCount(1);
                }
            }
        }
    }

}
